/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.stat.descriptive;

import genomemap.data.DataSource;

import javautil.collections.ArrayUtil;
import javautil.collections.ListUtil;
import javautil.lang.MathUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * This class deals with statistics that can be computed just from basic data manipulation
 * without using any sophisticated statistical techniques
 * @author stewari1
 */
public class HybridizationStatistics {

    /** Field description */
    private int totalPositiveSignals = -1;

    /** Field description */
    private int totalOffDiagonals = -1;

    /** Field description */
    private final int ch_id;

    /** Field description */
    private final int[] probeOrder;

    /** Field description */
    private final int[][] hybridData;

    /**
     * This contains the clone indices for a full map for the given probe order
     * For each probe onlyRegionCounts stores the count of clones that hybridized in the
     * only region. With <code>fullMapCloneIndices</code>, <code>onlyRegionCounts</code>
     * and <code>bothRegionCounts</code> we get an acurate picture of the full map.
     */
    private int[] fullMapCloneIndices;

    /** Field description */
    private final int[] onlyRegionCounts;

    /** Field description */
    private final int[] bothRegionCounts;

    /**
     * Constructs ...
     *
     * @param ch_id description
     * @param probeOrder description
     */
    public HybridizationStatistics(int ch_id, int[] probeOrder) {

        this.ch_id       = ch_id;
        this.probeOrder  = probeOrder;
        onlyRegionCounts = new int[probeOrder.length];
        bothRegionCounts = new int[probeOrder.length];
        this.hybridData  = DataSource.getPDataSource(ch_id, probeOrder).getHybridData();
    }

    public static void main(String[] args) {

        int ch_id                                 = 1;
        int[] probeOrder                          = {

            166, 21, 195, 219, 141, 87, 93, 115, 11, 230, 234, 148, 80, 235, 77, 135, 187, 143, 213,
            185, 12, 92, 83, 24, 203, 170, 133, 18, 165, 95, 74, 59, 154, 50, 90, 91, 66, 69, 9, 32,
            197, 182, 51, 46, 207, 217, 222, 44, 202, 4, 146, 19, 172, 220, 126, 139, 233, 27, 229,
            16, 63, 192, 84, 6, 56, 117, 225, 188, 191, 29, 173, 71, 179, 78, 36, 103, 125, 198,
            151, 3, 31, 134, 201, 138, 34, 33, 136, 149, 113, 121, 169, 7, 47, 114, 68, 128, 48,
            178, 8, 175, 167, 14, 130, 226, 99, 176, 53, 156, 158, 43, 221, 200, 72, 49, 223, 96,
            204, 211, 155, 107, 111, 55, 104, 94, 82, 45, 62, 35, 30, 147, 227, 124, 40, 224, 118,
            142, 150, 112, 5, 168, 97, 171, 60, 39, 37, 177, 210, 1, 206, 186, 88, 180, 26
        };
        HybridizationStatistics hybridizationStat = new HybridizationStatistics(ch_id, probeOrder);

        System.out.println("Total # of offDiagonals: " + hybridizationStat.getTotalOffDiagonals());
        System.out.println("Total # of signals: " + hybridizationStat.getTotalPositiveSignals());
        System.out.println("The % of offDiagonals: "
                           + hybridizationStat.getOffDiagonalPercentage());
    }

    public int getTotalPositiveSignals() {

        if (totalPositiveSignals == -1) {
            totalPositiveSignals = computeTotalPositiveSignals();
        }

        return totalPositiveSignals;
    }

    public int getTotalOffDiagonals() {

        if (totalOffDiagonals == -1) {
            totalOffDiagonals = computeTotalOffDiagonals();
        }

        return totalOffDiagonals;
    }

    public double getOffDiagonalPercentage() {
        return (double) getTotalOffDiagonals() / (hybridData.length * probeOrder.length) * 100;
    }

    /**
     * creates an on-the-fly (i.e., not stored) hybridyzation signals data arranged
     * in a full map for the given probe order.
     * @return
     */
    public int[][] getFullMapData() {
        return null;
    }

    /**
     * Method description
     *
     * @return description
     */
    private int computeTotalOffDiagonals() {

        List<Integer> cloneIndices = ListUtil.createArrayList(
                                         ArrayUtil.getIntegerArray(
                                             ArrayUtil.IntegerSequence(hybridData.length, true)));
        List<Integer> fullMapCloneIndexList = new ArrayList<>();

        for (int i = 0; i < probeOrder.length; i++) {

            List<Integer> onlyRegion = new ArrayList<>();
            List<Integer> bothRegion = new ArrayList<>();

            for (Integer cloneIndexObj : cloneIndices) {

                int cloneIndex = cloneIndexObj.intValue();

                if (hybridData[cloneIndex][probeOrder[i] - 1] == 1) {

                    if ((i < probeOrder.length - 1)
                            && (hybridData[cloneIndex][probeOrder[i + 1] - 1] == 1)) {


                        // both region
                        bothRegion.add(cloneIndexObj);
                    } else {


                        // only region
                        onlyRegion.add(cloneIndexObj);
                    }
                }
            }


            // add the region counts
            onlyRegionCounts[i] = onlyRegion.size();
            bothRegionCounts[i] = bothRegion.size();


            // add to the fullMap clone index list: first only region and then both region
            fullMapCloneIndexList.addAll(onlyRegion);
            fullMapCloneIndexList.addAll(bothRegion);


            // remove the used clones
            cloneIndices.removeAll(onlyRegion);
            cloneIndices.removeAll(bothRegion);
        }

        fullMapCloneIndices =
            ArrayUtil.getIntegerArray(fullMapCloneIndexList.toArray(new Integer[0]));

        System.out.println("Sum of onlyCounts: " + MathUtil.Sum(onlyRegionCounts));
        System.out.println("Sum of bothRegion Counts: " + MathUtil.Sum(bothRegionCounts));


        // compute the off-diagonals. Alongwith you can get the total signals as well.
        totalOffDiagonals    = 0;
        totalPositiveSignals = 0;

        int probeCounter = 0;
        int[] cumuCount  = MathUtil.cumulative(MathUtil.sum(onlyRegionCounts, bothRegionCounts));

        for (int rowIndex = 0; rowIndex < fullMapCloneIndices.length; rowIndex++) {

            int cloneIndex = fullMapCloneIndices[rowIndex];


            // redundant + 1 is kept for semantic meaning
            if (rowIndex + 1 == cumuCount[probeCounter] + 1) {
                probeCounter++;
            }

            for (int i = 0; i < probeOrder.length; i++) {


                // careful: tricky here.
                if ((i != probeCounter) && (i != probeCounter + 1)) {
                    if (hybridData[cloneIndex][probeOrder[i] - 1] == 1) totalOffDiagonals++;
                }

                if (hybridData[cloneIndex][probeOrder[i] - 1] == 1) totalPositiveSignals++;
            }
        }

        return totalOffDiagonals;
    }

    /**
     * Method description
     *
     * @return description
     */
    private int computeTotalPositiveSignals() {

        int totalSignalCount = 0;

        return totalSignalCount;
    }

    public int getCh_id() {
        return ch_id;
    }

    public int[] getProbeOrder() {
        return probeOrder;
    }
}
